package pl.edu.agh.gt.ga.greedy;

import org.apache.log4j.Logger;

import pl.edu.agh.gt.data.Rate;
import pl.edu.agh.gt.data.RatesHistory;
import pl.edu.agh.gt.ga.FitnessFunction;
import pl.edu.agh.gt.ga.Genotype;
import pl.edu.agh.gt.ga.Parameters;
import pl.edu.agh.gt.gui.ChartDisplay;
import pl.edu.agh.gt.stock.Stock;
import pl.edu.agh.gt.stock.Trader;
import pl.edu.agh.gt.stock.TraderListener;
import pl.edu.agh.gt.strategy.BuyAndHold;
import pl.edu.agh.gt.strategy.GreedyStrategy;
import pl.edu.agh.gt.strategy.Strategy;

/**
 * Beware! Objects of this class are used by multiple threads
 * @author kpajak
 *
 */
public class FitnessFunctionImpl implements FitnessFunction {

	private final static Logger log = Logger.getLogger(FitnessFunctionImpl.class);

	private final RatesHistory rates;

	private class TradingList implements TraderListener {
		
		private int traderPosClosingCounter;
		private int traderAllClosingCounter;
		private float traderLastTotal;	

		public TradingList (float bc) {
			traderLastTotal = bc;
		}
		@Override
		public void onClose(Trader trader, Rate rate) {
			if(trader.baseCurrency > traderLastTotal )
				traderPosClosingCounter++;
			
			traderAllClosingCounter++;
			traderLastTotal = trader.baseCurrency;
		}

		@Override
		public void onBuy(Trader trader, float base, Rate rate) {}
		@Override
		public void onSell(Trader trader, float quote, Rate rate) {}
	}

	public FitnessFunctionImpl(RatesHistory h) {
		rates = h;
	}

	/** 
	 * Don't modify fields here,
	 * this method is used in multithread env.
	 */
	@Override
	public float estimate(Genotype genotype, Parameters params) {

		GreedyStrategy strategy = decode(genotype);
		float fitness = 0;
		try {

			log.debug("Analizing strategy: " + strategy);

			final Stock stock = new Stock(rates);
			final Trader greedyTrader = new Trader("TraderX", stock, strategy);

			TradingList list = new TradingList(greedyTrader.baseCurrency);
			greedyTrader.setListener(list);
			
			stock.addTrader(greedyTrader);

			stock.runStock();

			float greedyTraderValue = greedyTrader.evaluate(stock.getRatesHistory().getLastRate());
/*
			stock.removeTraders();

			BuyAndHold bAndH = new BuyAndHold();
			bAndH.initialInvestment = Math.min(greedyTrader.maxInvested, Trader.INITIAL_VALUE);
			Trader bAndHTrader = new Trader("Buy&Hold", stock, bAndH);
			stock.addTrader(bAndHTrader);

			stock.runStock();

			float bAndHTraderValue = bAndHTrader.evaluate(stock.getRatesHistory().getLastRate());
*/
			
			fitness = (greedyTraderValue - Trader.INITIAL_VALUE) / greedyTrader.maxInvested;
			//fitness = (greedyTraderValue) - (bAndHTraderValue);
			//fitness = (float)list.traderPosClosingCounter * (greedyTraderValue - Trader.INITIAL_VALUE); // ciekawostka

			log.debug("Stock simulation finished, trader total:" + greedyTraderValue + ", maxInvested: " + greedyTrader.maxInvested);
			log.debug("FITNESS = " + fitness);

		} catch (Exception e) {
			log.error("Stock exception", e); // XXX wrap & throw
		}

		return fitness;
	}

	public void showGenotypeTradingComparedToBuyAndHold(Genotype genotype) throws Exception {

		// Logger.getLogger("pl.edu.agh.gt.strategy").setLevel(Level.DEBUG);
		// Logger.getLogger("pl.edu.agh.gt.stock").setLevel(Level.DEBUG);
		Stock stock = new Stock(rates);

		Strategy strategy = decode(genotype);
		Trader trader1 = new Trader("Greedy", stock, strategy);
		trader1.setLogTrading(true);
		stock.removeTraders();
		stock.addTrader(trader1);
		stock.runStock();

		log.info("Trader: maxInvested = " + trader1.maxInvested + ", \tearned: "
				+ (trader1.baseCurrency - Trader.INITIAL_VALUE) + ",\tbuy actions: " + trader1.buyActions
				+ ",\tsell actions: " + trader1.sellActions + ",\tclose actions: " + trader1.closeActions);

		BuyAndHold bAndH = new BuyAndHold();
		bAndH.initialInvestment = Math.min(trader1.maxInvested, Trader.INITIAL_VALUE);
		Trader trader2 = new Trader("Buy&Hold", stock, bAndH);
		trader2.setLogTrading(true);
		stock.removeTraders();
		stock.addTrader(trader2);
		stock.runStock();

		ChartDisplay.tradingChart("Strategy " + strategy, stock.getRatesHistory(), trader1.getTradingHistory(),
				trader2.getTradingHistory());
	}

	public static GreedyStrategy decode(Genotype genotype) {

		GreedyStrategy strategy = new GreedyStrategy();

		// strategy.resolution = (Integer) genotype.getGene("resolution");
		strategy.greedA = (Float) genotype.getGene("greedA");
		// strategy.greedB = (Float) genotype.getGene("greedB");
		//strategy.maxLoss = (Float) genotype.getGene("maxLoss");
		//strategy.desiredEarn = (Float) genotype.getGene("desiredEarn");
		strategy.enteringValue = (Float) genotype.getGene("enteringValue");
		strategy.lossTolerancy = (Float) genotype.getGene("lossTolerancy");

		return strategy;
	}

}
